/*
 * SmartCam master processor library
 * Custom skeleton thread spawning.
 */

#define _XOPEN_SOURCE 500

#include <log.h>
#include "globals.h"
#include "management.h"

scos_mutex_t thr_mutex;
scos_cond_t thr_cond;
sc_thread_t *thr_thread;

void scThrInit(void)
{
  lprintf(STREAM(scl.notice), "Initializing custom threads");

  scos_mutex_init(&thr_mutex);
  scos_cond_init(&thr_cond);
}

void *scThrRun(void *param)
{
  sc_cusparam_t *p = (sc_cusparam_t*)param;
  sc_argdecode_t *d;
  void *addr[9];
  int id;
  sc_thread_t *t, *this_thread;

  logRegisterThread(p->text);

  this_thread = thr_thread = scManAddThread();

  scos_mutex_lock(&thr_mutex);
  scos_cond_signal(&thr_cond);
  scos_mutex_unlock(&thr_mutex);

  scManAcquireLock();
  scManShedLock();

  lprintf(STREAM(scl.debug), "Custom thread started with %d arguments",
	  tblgetelements(&p->args));

  abortOnError(STREAM(scl.err), tblgetelements(&p->args) >= 10);

  id = -1;
  while ((id = tblgetnexti(&p->args, id)) >= 0)
  {
    abortOnNULL(STREAM(scl.err), tblget(&p->args, id, &d));
    addr[id] = d->address;
  }

  switch (tblgetelements(&p->args))
  {
    case 0: p->op();
      break;
    case 1: p->op(addr[0]);
      break;
    case 2: p->op(addr[0], addr[1]);
      break;
    case 3: p->op(addr[0], addr[1], addr[2]);
      break;
    case 4: p->op(addr[0], addr[1], addr[2], addr[3]);
      break;
    case 5: p->op(addr[0], addr[1], addr[2], addr[3], addr[4]);
      break;
    case 6: p->op(addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
      break;
    case 7: p->op(addr[0], addr[1], addr[2], addr[3], addr[4], addr[5], addr[6]);
      break;
    case 8: p->op(addr[0], addr[1], addr[2], addr[3], addr[4], addr[5], addr[6], addr[7]);
      break;
    case 9: p->op(addr[0], addr[1], addr[2], addr[3], addr[4], addr[5], addr[6], addr[7],
		  addr[8]);
      break; 
  }

  lprintf(STREAM(scl.info), "Custom thread done");

  id = -1;
  while ((id = tblgetnexti(&p->args, id)) >= 0)
  {
    abortOnNULL(STREAM(scl.err), tblget(&p->args, id, &d));
    _finalize(d->address);
    free(d->address);
  }

  tbldestroy(&p->args, 1);
  free(p);

  scManAcquireLock();

  scManDestroyThread();

  id = -1;
  while (tblgetnextelement(&sc_threads, &id, &t))
    if (!t->blocked)
      break;

  scManShedLock();

  if (id < 0)
    _schedule(1);

  return NULL;
}
